import axios, {AxiosError, AxiosRequestConfig, AxiosResponse, InternalAxiosRequestConfig} from "axios";
import {ElMessage, ElMessageBox} from "element-plus";
import store from "@/store";
import {env} from './config'

const {API_URL, TIMEOUT, TOKEN_NAME} = env

// axios实例
const instance = axios.create({
    baseURL: API_URL,
    timeout: TIMEOUT? Number.parseInt(TIMEOUT) : 3000,
    responseType: 'json',
    validateStatus(status) {
        // 200 外的状态码都认定为失败
        return status === 200;
    }
});

// 请求拦截器
instance.interceptors.request.use((config: InternalAxiosRequestConfig) => {
    // 请求头携带token
    const {token} = store.userStore;
    if (token) {
        config.headers[TOKEN_NAME] = token;
    }
    return config;
}, (error: AxiosError) => {
    return Promise.reject(error)
});

// 响应拦截器
instance.interceptors.response.use((response: AxiosResponse) => {

    const {data, headers} = response;

    /*console.log('%c!<-------------------- 响应拦截 s -------------------->', 'color:blue')
    console.log(response)
    console.log('%c!<-------------------- 响应内容 c -------------------->', 'color:blue')
    console.log(data)
    console.log('%c!<-------------------- 响应拦截 e -------------------->', 'color:blue')*/

    switch (data.code) {
        case 401:
            ElMessageBox.confirm("当前页面已失效，请重新登录", "提示", {
                confirmButtonText: "确定",
                type: "warning",
            }).then(() => {
                store.userStore?.setToken("");
                location.reload();
            });
            break;
        default:
            data.message && ElMessage(data);
            break;
    }

    //判断是否为文件下载操作
    if (['application/application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', 'application/octet-stream'].includes(headers['content-type'])) {
        return {data: data, filename: headers['content-filename']};
    } else {
        return data;
    }
}, (error: AxiosError) => {
    const status = error.response?.status || error.code;
    switch (status) {
        case 401:
        case 403:
            const rs = error.response?.data as any;
            ElMessage.error(rs.message);
            if (rs.code === 461) {
                store.userStore.setToken("");
                location.reload();
            }
            return;
        case 500:
            ElMessage.error('系统内部异常,请联系管理员');
            break;
        case 51001:
            ElMessage.error('磁盘上未找到文件,请联系管理员');
            break;
        case 51002:
            ElMessage.error('页面超时,请联刷新后再试');
            break;
        case 'ECONNABORTED':
            ElMessage.error('响应超时,请稍候再试');
            break;
        case 'ERR_CANCELED':
            ElMessage.success('操作取消成功');
            break;
        default:
            ElMessage.error(error.message);
    }
    return Promise.reject(status);
});

/**
 * 参数转换
 * @param params
 */
function transform (params: any): string{
    let result = '' as string;
    if (typeof params !== 'string') {
        Object.entries(params).forEach(([k, v]) => {
            if (!Object.is(v, undefined) && !Object.is(v, null)) {
                result += `${encodeURIComponent(k)}=${encodeURIComponent(v as any)}&`;
            }
        })
    } else {
        result = params;
    }
    return result
}

export const service = {
    /**
     * 发送GET请求的方法，可指定数据类型T，并返回一个Promise对象，其值为Result<T>类型
     * @param url 请求地址
     * @param data 参数
     * @param loading
     */
    get<T = any>(url: string, data?: any): Promise<IResponse<T>> {
        if (Object.is(data, undefined)) {
            return instance.get<T, IResponse<T>>(url);
        } else {
            return instance.get<T, IResponse<T>>(`${url}${url.indexOf('?') !== -1? '&':'?'}${transform(data)}`);
        }
    },

    /**
     * 发送POST请求的方法，同样可指定数据类型T，并返回一个Promise对象
     * @param url 请求地址
     * @param data 参数
     * @param config 配置项
     */
    post<T = any>(url: string, data: any, config?: AxiosRequestConfig<any>): Promise<IResponse<T>> {
        return instance.post<T, IResponse<T>>(url, data, {
            transformRequest: [params => transform(params)],
            headers: {
                'Content-Type': 'application/x-www-form-urlencoded'
            },
            ...config
        });
    },

    /**
     * 发送PUT请求的方法，同样可指定数据类型T，并返回一个Promise对象
     * @param url 请求地址
     * @param data 参数
     * @param config 配置项
     */
    put<T = any>(url: string, data: any, config?: AxiosRequestConfig<any>): Promise<IResponse<T>> {
        return instance.put<T, IResponse<T>>(url, data, {
            transformRequest: [params => transform(params)],
            headers: {
                'Content-Type': 'application/x-www-form-urlencoded'
            },
            ...config
        });
    },

    /**
     * 发送DELETE请求的方法，同样可指定数据类型T，并返回一个Promise对象
     * @param url  请求地址
     * @param data 参数
     */
    delete<T = any>(url: string, data?: any): Promise<IResponse<T>> {
        if (Object.is(data, undefined)) {
            return instance.delete<T, IResponse<T>>(`${url}`);
        } else {
            return instance.delete<T, IResponse<T>>(`${url}${url.indexOf('?') !== -1? '&':'?'}${transform(data)}`);
        }
    },

    /**
     * 文件下载
     * @param url 请求地址
     * @param data 参数
     * @param config 配置项
     */
    download (url: string, data?: any, config?: AxiosRequestConfig<any>): Promise<any>{
        return instance.post(url, data, {
            transformRequest: [params => transform(params)],
            responseType: 'blob',
            ...config
        }).then((res:any) => {
            const blob = new Blob([res.data]);
            if ('download' in document.createElement('a')) {
                const link = document.createElement('a');
                link.href = URL.createObjectURL(blob);
                link.download = decodeURI(res['filename']);
                link.style.display = 'none';
                document.body.appendChild(link);
                link.click();
                URL.revokeObjectURL(link.href);
                document.body.removeChild(link);
            } else {
                // 兼容 edge 不支持 createObjectURL 方法
                if ("msSaveBlob" in  navigator) {
                    return window.navigator.msSaveBlob(blob, res['filename'] as string);
                }
            }
            return res;
        });
    },


    /**
     * 通过消息弹窗发送请求并决定是否执行（默认发送DELETE请求）
     * @param message 提示内容
     * @param options 配置
     * @param doing 回调函数
     */
    confirm<T = any>(message: string, options: AxiosConfirmRequestOptions, doing: ()=>void = () => ({})): void {
        ElMessageBox.confirm(message, '提示', {
            type: 'warning',
            draggable: true,
            beforeClose: (action, instance, done) => {
                if (action === 'confirm') {
                    instance.confirmButtonLoading = true
                    service[options.type? options.type:'delete']<T>(options.url, options.data).then(data => {
                        if (data.code === 200) {
                            options.success?.(data)
                        } else {
                            options.fail?.(data);
                        }
                    }).catch((e) => {
                        console.error(e)
                    }).finally(() => {
                        instance.confirmButtonLoading = false;
                        doing();
                        done();
                    });
                } else {
                    done()
                }
            }
        }).catch((e) => console.error(e));
    },
};